﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Data.SqlClient;
using System.Data;
using Infrastructure.Interface;

namespace Domain.Repository
{
    /// <summary>
    /// 仓储基础类
    /// <remarks></remarks>
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    public class RepositoryBase<T> : IRepository<T>, ICRUDPable<T> where T : EntityBase
    {
        #region 属性
        /// <summary>
        /// 获取仓储数据库操作上下文对象
        /// 外键关联实体需要手动Include
        /// </summary>
        protected RepositoryContext Context { get; private set; }

        /// <summary>
        /// 获取表操作对象
        /// </summary>
        protected DbSet<T> DbSet { get; private set; }

        #endregion

        #region 构造器
        /// <summary>
        /// 仓储基础类
        /// </summary>
        /// <param name="context">上下文</param>
        public RepositoryBase(IRepositoryContext context)
        {
            this.Context = context as RepositoryContext;
            this.DbSet = this.Context.Set<T>();
        }
        #endregion

        #region IRepository成员

        /// <summary>
        /// 提交更变
        /// </summary>
        /// <returns></returns>
        int IUnitOfWork.Commit()
        {
            return this.Context.SaveChanges();
        }

        /// <summary>
        /// 清除上下文本地缓存
        /// </summary>
        void IRepository<T>.ClearLocalCache()
        {
            this.DbSet.Local.Clear();
        }
        #endregion

        #region 查找方法
        /// <summary>
        /// 查找对象
        /// </summary>
        /// <param name="id">实体的ID</param>
        /// <returns></returns>
        public virtual T Find(Guid id)
        {
            return this.DbSet.Find(id);
        }

        /// <summary>
        /// 获取所有实体对象
        /// </summary>
        /// <typeparam name="TNew">投影类型</typeparam>
        /// <param name="where">条件</param>
        /// <param name="selector">选择器</param>
        /// <param name="orderByString">排序字符串，为null则不排</param>
        /// <returns></returns>
        public virtual List<TNew> FindAll<TNew>(Expression<Func<T, bool>> where, Expression<Func<T, TNew>> selector, string orderByString) where TNew : class
        {
            if (string.IsNullOrEmpty(orderByString))
            {
                return this.DbSet.Where(where).Select(selector).ToList();
            }
            return this.DbSet.Where(where).Select(selector).OrderBy(orderByString).ToList();
        }

        /// <summary>
        /// 获取前N条数据
        /// </summary>
        /// <typeparam name="TNew">投影类型</typeparam>
        /// <param name="where">条件</param>
        /// <param name="selector">选择器</param>
        /// <param name="orderByString">排序字符串</param>
        /// <param name="count">选择记录条数</param>
        /// <returns></returns>
        public virtual List<TNew> FindTop<TNew>(Expression<Func<T, bool>> where, Expression<Func<T, TNew>> selector, string orderByString, int count) where TNew : class
        {
            return this.DbSet.Where(where).Select(selector).OrderBy(orderByString).Take(count).ToList();
        }
        #endregion

        #region 增加方法
        /// 添加实体
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public virtual void Add(T entity)
        {
            this.DbSet.Add(entity);
        }

        /// <summary>
        /// 添加实体
        /// </summary>
        /// <param name="entitys">多个实体</param>
        /// <returns></returns>
        public virtual void Add(IEnumerable<T> entitys)
        {
            Context.Configuration.AutoDetectChangesEnabled = false;//解决大批量性能问题
            this.DbSet.Add(entitys);
            Context.Configuration.AutoDetectChangesEnabled = true;
        }
        #endregion

        #region 删除方法
        /// <summary>
        /// 移除实体
        /// </summary>
        /// <param name="id">实体ID</param>
        /// <returns></returns>
        public virtual void Remove(Guid id)
        {
            this.DbSet.Remove(id);
        }

        /// <summary>
        /// 移除实体
        /// </summary>
        /// <param name="ids">多个实体的ID</param>
        /// <returns></returns>
        public virtual void Remove(IEnumerable<Guid> ids)
        {
            this.DbSet.Remove(ids);
        }
        /// <summary>
        /// 移除实体
        /// </summary>
        /// <param name="entityArray">多个实体</param>
        /// <returns></returns>
        public virtual void Remove(IEnumerable<T> entityArray)
        {
            this.DbSet.Remove(entityArray);
        }

        /// <summary>
        /// 批量移除
        /// </summary>
        /// <param name="filter">条件</param>
        /// <returns></returns>
        public virtual void Remove(Expression<Func<T, bool>> filter)
        {
            this.DbSet.Remove(filter);
        }

        /// <summary>
        /// 通过存储过程删除数据
        /// </summary>
        /// <param name="id">实体id</param>
        /// <param name="spName">存储过程名，默认值是：proc_实体名_del</param>
        /// <returns>操作后的结果</returns>
        public virtual string DeleteBySP(Guid id, string spName = null)
        {
            //实体名
            string typeName = typeof(T).Name.ToLower();
            SqlParameter[] paras = new SqlParameter[] { new SqlParameter("@id", id) };//参数
            //执行后返回的结果
            string result = "删除失败";
            int affectRow = 0;
            //生成存储过程名字
            if (string.IsNullOrEmpty(spName))
            {
                spName = string.Format("exec proc_{0}_del @id", typeName);
            }
            else
            {
                spName = spName + " @id";
            }
            try
            {
                affectRow = this.Context.Database.ExecuteSqlCommand(spName, paras);
            }
            catch (SqlException ex)
            {
                if (ex.Class == 16 && ex.State == 1) //是手动引发的异常 
                {
                    result = ex.Message;
                }
            }
            catch (Exception ex)
            {

            }
            return affectRow >= 1 ? "删除成功" : result;
        }

        /// <summary>
        /// 通过存储过程批量删除数据
        /// </summary>
        /// <param name="ids">id的字符串逗号分隔（即js 数组toString()的数据）</param>
        /// <param name="delList">把成功删除实体添加到实例化的集合delList中</param>
        ///<param name="spName">存储过程名称</param>
        /// <returns>操作后的结果</returns>
        public virtual string DeleteBySP(string ids, ref List<T> delList, string spName = null)
        {
            string result = "删除失败";
            if (string.IsNullOrEmpty(ids) == false && ids.Length >= 32)
            {
                //要删除的id数组
                string[] arrID = ids.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                List<Guid> guids = new List<Guid>();//需要删除guid列表
                arrID.ToList().ForEach(id => { if (id.Length == 36) guids.Add(Guid.Parse(id)); });
                //不为空时查出要删除id的实体集合
                if (delList != null)
                {
                    delList = this.FindAll(e => guids.Contains(e.ID), e => e, null);
                }
                List<Guid> unSuccessIDs = new List<Guid>();//删除失败的ids

                //逐条删除
                guids.ForEach(id =>
                {
                    result = this.DeleteBySP(id, spName);
                    if (result.Contains("删除成功") == false)
                    {
                        unSuccessIDs.Add(id);
                    }
                });

                #region 对返回结果赋值
                if (unSuccessIDs.Count == 0)
                {
                    result = "删除成功";
                }
                else
                {
                    if (guids.Count > 1 && unSuccessIDs.Count > 0) //删除多条记录
                    {
                        result = string.Format("成功删除记录{0}条，失败{1}条！", guids.Count - unSuccessIDs.Count, unSuccessIDs.Count);
                    }

                    //对delList移除删除失败的记录
                    if (delList != null)
                    {
                        var hasDelList = delList;
                        unSuccessIDs.ForEach(uID =>
                        {
                            foreach (var item in hasDelList)
                            {
                                if (item.ID == uID)
                                {
                                    hasDelList.Remove(item);//从全部要删除的实体中，移除删除失败的，剩下就是删除成功的
                                    break;
                                }
                            }
                        });
                        delList = hasDelList;
                    }
                }
                #endregion
            }

            return result;
        }


        /// <summary>
        /// 通过存储过程删除时间范围内数据
        /// </summary>
        /// <param name="starttime">开始时间</param>
        /// <param name="endtime">结止时间</param>
        /// <param name="spname">存储结构名称</param>
        /// <param name="message">执行结果</param>
        /// <returns>返回需要删除文件列表</returns>
        public List<string> DeleteAllBySP(int systemid, DateTime starttime, DateTime endtime, string spname, out string message)
        {
            var list = new List<string>();
            endtime = endtime.AddDays(0.999);
            var table = this.DeleteAllBySP(Context.Database, systemid, starttime, endtime, "proc_" + spname + "_delall", out message);
            if (table != null)
            {
                foreach (DataRow row in table.Rows)
                {
                    list.Add(row[0].ToString());
                }
            }
            return list;
        }

        private DataTable DeleteAllBySP(Database database, int systemid, DateTime starttime, DateTime endtime, string spname, out string message)
        {
            message = string.Empty;
            SqlParameter[] paras = new SqlParameter[]
            { 
                new SqlParameter("@systemid",systemid),
                new SqlParameter("@starttime", starttime), 
                new SqlParameter("@endtime", endtime), 
                new SqlParameter("@message", message) 
            };//参数
            paras[3].Direction = ParameterDirection.Output;
            using (SqlConnection con = new SqlConnection(database.Connection.ConnectionString))
            {
                using (SqlCommand cmd = new SqlCommand(spname, con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddRange(paras);
                    con.Open();
                    using (SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd))
                    {
                        using (DataSet dataSet = new DataSet())
                        {
                            dataAdapter.Fill(dataSet);
                            message = paras[3].Value.ToString();
                            if (dataSet.Tables.Count > 0)
                            {
                                return dataSet.Tables[0];
                            }
                            return null;
                        }
                    }
                }
            }
        }


        #endregion

        #region 更新方法
        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public virtual void Update(T entity)
        {
            this.DbSet.Update(this.Context, entity);
        }

        /// <summary>
        /// 批量更新
        /// </summary>
        /// <param name="updateExpression">更新表示式</param>
        /// <returns></returns>
        public virtual void Update(Expression<Func<T, T>> updateExpression)
        {
            this.DbSet.Update(updateExpression);
        }


        /// <summary>
        /// 批量更新
        /// </summary>
        /// <param name="filter">过滤条件</param>
        /// <param name="updateExpression">更新表达式</param>
        /// <returns></returns>
        public virtual void Update(Expression<Func<T, bool>> filter, Expression<Func<T, T>> updateExpression)
        {
            this.DbSet.Update(filter, updateExpression);
        }
        #endregion

        #region 分页方法
        /// <summary>
        /// 分页的基础方法
        /// </summary>
        /// <typeparam name="TNew">投影的类型</typeparam>
        /// <param name="where">条件</param>
        /// <param name="selector">选择器</param>       
        /// <param name="orderByString">排序字符串</param>
        /// <param name="pageIndex">页面索引</param>
        /// <param name="pageSize">页面大小</param>
        /// <returns></returns>
        public virtual PageBase<TNew> ToPage<TNew>(Expression<Func<T, bool>> where, Expression<Func<T, TNew>> selector, string orderByString, int pageIndex, int pageSize) where TNew : class
        {
            return this.DbSet.Where(where).Select(selector).OrderBy(orderByString).ToPage(pageIndex, pageSize);
        }

        /// <summary>
        /// 分页的基础方法
        /// 数据合为一页
        /// </summary>
        /// <param name="where">条件</param>
        /// <param name="selector">选择器</param>
        /// <param name="orderByString">排序字符串</param>
        /// <returns></returns>
        public virtual PageBase<TNew> ToPage<TNew>(Expression<Func<T, bool>> where, Expression<Func<T, TNew>> selector, string orderByString) where TNew : class
        {
            return this.DbSet.Where(where).Select(selector).OrderBy(orderByString).ToPage();
        }
        #endregion
    }
}
